use core::future::Future;
use std::pin::Pin;

use futures_util::future::Ready;

use super::{
    request::{Body, Request, ServiceRequest},
    response::Response,
};

type BoxFuture<T> = Pin<Box<dyn Future<Output = T> + Send>>;

pub trait FromRequest: Sized {
    type Future: Future<Output = Result<Self, anyhow::Error>> + Send + 'static;
    fn from_request(s: &Request, body: &mut Option<Body>) -> Self::Future;
}

pub trait Handler<Args>: Send + Clone + 'static {
    type Output;
    type Future: Future<Output = Self::Output> + Send + 'static;
    fn call(&self, a1: Args) -> Self::Future;
}

macro_rules! factory_tuple ({ $($param:ident)* } => {
    impl<Func, Fut, $($param,)*> Handler<($($param,)*)> for Func
    where
        Func: Fn($($param),*) -> Fut + Clone + Send + 'static,
        Fut: Future + Send + 'static,
    {
        type Output = Fut::Output;
        type Future = Fut;

        #[inline]
        #[allow(non_snake_case)]
        fn call(&self, ($($param,)*): ($($param,)*)) -> Self::Future {
            (self)($($param,)*)
        }
    }
});

factory_tuple! {}
factory_tuple! { A }
factory_tuple! { A B }
factory_tuple! { A B C }
factory_tuple! { A B C D }
factory_tuple! { A B C D E }
factory_tuple! { A B C D E F }
factory_tuple! { A B C D E F G }
factory_tuple! { A B C D E F G H }
factory_tuple! { A B C D E F G H I }
factory_tuple! { A B C D E F G H I J }
factory_tuple! { A B C D E F G H I J K }
factory_tuple! { A B C D E F G H I J K L }

// impl<F, T, Fut> Handler<(T,)> for F
// where
//     F: Fn(T) -> Fut + Clone + 'static,
//     Fut: Future,
// {
//     fn call(&self, a1: (T,)) -> Self::Future {
//         (self)(a1.0)
//     }

//     type Output = Fut::Output;

//     type Future = Fut;
// }

pub trait ServiceFactory: Send + Sync + 'static {
    type InitError;
    type Future: Future<Output = Result<Response, Self::InitError>>;
    fn new_service(&self, req: ServiceRequest) -> Self::Future;
}

#[derive(Clone)]
pub struct FnServiceFactory<F, Fut, Err>
where
    F: Fn(ServiceRequest) -> Fut + Sync,
    Fut: Future<Output = Result<Response, Err>>,
    Fut: Send + 'static,
{
    f: F,
}

impl<F, Fut, Err> FnServiceFactory<F, Fut, Err>
where
    F: Fn(ServiceRequest) -> Fut + Sync,
    Fut: Future<Output = Result<Response, Err>> + Send + 'static,
{
    pub fn new(f: F) -> Self {
        // axum::handler::Handler
        FnServiceFactory { f }
    }
}

impl<F, Fut, Err> ServiceFactory for FnServiceFactory<F, Fut, Err>
where
    F: Fn(ServiceRequest) -> Fut + Sync + Send + 'static,
    Fut: Future<Output = Result<Response, Err>> + Send + 'static,
    Err: Send + 'static,
{
    fn new_service(&self, req: ServiceRequest) -> Self::Future {
        Box::pin((self.f)(req))
    }

    type InitError = Err;

    type Future = BoxFuture<Result<Response, Self::InitError>>; // Result<Response, Self::InitError>;
}

/* pub fn router_to<F, T>(f: F) -> BoxedServiceFactory
where
    F: Handler<T, Output = anyhow::Result<Response>>,
    T: FromRequest,
{
    // let i = f.call(T::from_request("123"));
    // println!("{i:?}");
    Box::new(FnServiceFactory::new(move |req| {
        let handler = f.clone();
        async move {
            match T::from_request(&req).await {
                Err(_err) => match req.protocol() {
                    Protocol::Http1_1 => Ok(Response::http_ok()),
                    Protocol::Rtsp1_0 => Ok(Response::rtsp_ok(&req)),
                    _ => Ok(Response::http_ok()),
                },
                Ok(data) => handler.call(data).await,
            }
        }
    }))
} */

#[allow(non_snake_case)]
mod tuple_from_req {
    use super::*;
    use pin_project_lite::pin_project;
    use std::task::{Context, Poll};

    type Error = anyhow::Error;

    macro_rules! tuple_from_req {
        ($fut: ident; $($T: ident),*) => {
            /// FromRequest implementation for tuple
            #[allow(unused_parens)]
            impl<$($T: FromRequest + Send + 'static),+> FromRequest for ($($T,)+)
            {
                type Future = $fut<$($T),+>;

                fn from_request(req: &Request, body: &mut Option<Body>) -> Self::Future {
                    $fut {
                        $(
                            $T: ExtractFuture::Future {
                                fut: $T::from_request(req, body)
                            },
                        )+
                    }
                }
            }

            pin_project! {
                pub struct $fut<$($T: FromRequest),+> {
                    $(
                        #[pin]
                        $T: ExtractFuture<$T::Future, $T>,
                    )+
                }
            }

            impl<$($T: FromRequest),+> Future for $fut<$($T),+>
            {
                type Output = Result<($($T,)+), Error>;

                fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
                    let mut this = self.project();

                    let mut ready = true;
                    $(
                        match this.$T.as_mut().project() {
                            ExtractProj::Future { fut } => match fut.poll(cx) {
                                Poll::Ready(Ok(output)) => {
                                    let _ = this.$T.as_mut().project_replace(ExtractFuture::Done { output });
                                },
                                Poll::Ready(Err(e)) => return Poll::Ready(Err(e.into())),
                                Poll::Pending => ready = false,
                            },
                            ExtractProj::Done { .. } => {},
                            ExtractProj::Empty => unreachable!("FromRequest polled after finished"),
                        }
                    )+

                    if ready {
                        Poll::Ready(Ok(
                            ($(
                                match this.$T.project_replace(ExtractFuture::Empty) {
                                    ExtractReplaceProj::Done { output } => output,
                                    _ => unreachable!("FromRequest polled after finished"),
                                },
                            )+)
                        ))
                    } else {
                        Poll::Pending
                    }
                }
            }
        };
    }

    pin_project! {
        #[project = ExtractProj]
        #[project_replace = ExtractReplaceProj]
        enum ExtractFuture<Fut, Res> {
            Future {
                #[pin]
                fut: Fut
            },
            Done {
                output: Res,
            },
            Empty
        }
    }

    impl FromRequest for () {
        type Future = Ready<Result<Self, Error>>;

        fn from_request(_: &Request, _: &mut Option<Body>) -> Self::Future {
            futures_util::future::ok(())
        }
    }

    tuple_from_req! { TupleFromRequest1; A }
    tuple_from_req! { TupleFromRequest2; A, B }
    tuple_from_req! { TupleFromRequest3; A, B, C }
    tuple_from_req! { TupleFromRequest4; A, B, C, D }
    tuple_from_req! { TupleFromRequest5; A, B, C, D, E }
    tuple_from_req! { TupleFromRequest6; A, B, C, D, E, F }
    tuple_from_req! { TupleFromRequest7; A, B, C, D, E, F, G }
    tuple_from_req! { TupleFromRequest8; A, B, C, D, E, F, G, H }
    tuple_from_req! { TupleFromRequest9; A, B, C, D, E, F, G, H, I }
    tuple_from_req! { TupleFromRequest10; A, B, C, D, E, F, G, H, I, J }
    tuple_from_req! { TupleFromRequest11; A, B, C, D, E, F, G, H, I, J, K }
    tuple_from_req! { TupleFromRequest12; A, B, C, D, E, F, G, H, I, J, K, L }
}

pub type BoxedServiceFactory = Box<
    dyn ServiceFactory<
        InitError = anyhow::Error,
        Future = BoxFuture<Result<Response, anyhow::Error>>,
    >,
>;
